#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <pthread.h>
#include <sys/unistd.h>
#include <windows.h>

typedef enum Direction Direction;

typedef enum GameState GameState;

typedef struct SnakeBody SnakeBody;

typedef struct GameData GameData;

enum Direction {
    UP,
    DOWN,
    LEFT,
    RIGHT
};

enum GameState {
    START,
    END
};

struct SnakeBody {
    int x;
    int y;

    SnakeBody *next;
};

struct GameData {
    int row;
    int column;
    int score;

    int x_food;
    int y_food;

    pthread_t pid;

    SnakeBody *head;
    Direction direction;


    GameState state;

    char **map;
};

void startGame();                               // 开始游戏

void endGame(GameData *game_data);              // 结束游戏

GameData *initData();                           // 初始化数据

void initMap(GameData *game_data);              // 初始化地图

void initSnake(GameData *game_data);            // 初始化蛇

void createFood(GameData *game_data);           // 生成食物坐标

void snakeMove(GameData *game_data);            // 蛇移动

void drawMap(GameData *game_data);              // 画出地图

void *getDirectionAsyn(void *args);             // 异步获取蛇的方向

void showCursor(bool visible);                  // 显示或隐藏光标

int main() {
    startGame();

    return 0;
}

void startGame() {
    GameData *game_data = initData();

    initMap(game_data);
    initSnake(game_data);
    createFood(game_data);

    // 创建方向获取的异步函数
    pthread_create(&game_data->pid, NULL, getDirectionAsyn, game_data);

    int count = 1000;
    while (count--) {
        snakeMove(game_data);
        drawMap(game_data);
        usleep(200 * 1000);
    }

}

void endGame(GameData *game_data) {
    // 回收异步获取蛇方向的线程
    pthread_join(game_data->pid, NULL);
    exit(0);
}

GameData *initData() {
    GameData *game_data = (GameData *) malloc(sizeof(GameData));

    game_data->row = 15;
    game_data->column = 50;
    game_data->score = 0;
    game_data->direction = RIGHT;
    game_data->state = START;

    srand(time(NULL));

    return game_data;
}

void initMap(GameData *game_data) {
    int row = game_data->row;
    int column = game_data->column;

    char (*map)[column] = malloc(sizeof(char) * row * column);
    memset(map, ' ', sizeof(char) * row * column);
    for (int i = 0; i < column; ++i) {
        map[0][i] = map[row - 1][i] = '+';
    }
    for (int i = 1; i < row - 1; i++) {
        map[i][0] = map[i][column - 1] = '+';
    }

    game_data->map = (char **) map;
}

void initSnake(GameData *game_data) {
    int row = game_data->row;
    int column = game_data->column;
    char (*map)[column] = (char (*)[column]) game_data->map;

    int x_head = row / 2;
    int y_head = column / 2;

    map[x_head][y_head - 2] = '*';
    map[x_head][y_head - 1] = '*';
    map[x_head][y_head] = '#';

    SnakeBody *p = NULL;
    SnakeBody *body = NULL;

    SnakeBody *head = (SnakeBody *) malloc(sizeof(SnakeBody));
    head->x = x_head;
    head->y = y_head;
    head->next = NULL;
    game_data->head = head;
    p = head;

    body = (SnakeBody *) malloc(sizeof(SnakeBody));
    body->x = x_head;
    body->y = y_head - 1;
    body->next = NULL;
    p->next = body;
    p = body;

    body = (SnakeBody *) malloc(sizeof(SnakeBody));
    body->x = x_head;
    body->y = y_head - 2;
    body->next = NULL;
    p->next = body;
}

void createFood(GameData *game_data) {
    int row = game_data->row;
    int column = game_data->column;

    int x_food;
    int y_food;

    while (true) {
        x_food = (rand() % (game_data->row - 2)) + 1;
        y_food = (rand() % (game_data->column - 2)) + 1;

        SnakeBody *p = game_data->head;
        while (p != NULL) {
            if (x_food == p->x && y_food == p->y) {
                break;
            }
            p = p->next;
        }

        if (p == NULL) {
            break;
        }
    }

    char (*map)[column] = (char (*)[column]) game_data->map;

    game_data->x_food = x_food;
    game_data->y_food = y_food;

    map[x_food][y_food] = '@';
}

void snakeMove(GameData *game_data) {
    int row = game_data->row;
    int column = game_data->column;

    Direction direction = game_data->direction;
    SnakeBody *head = game_data->head;

    char (*map)[column] = (char (*)[column]) game_data->map;

    // 获取蛇的下一个方向的坐标
    int x_next = head->x;
    int y_next = head->y;

    if (direction == UP) {
        x_next--;

    } else if (direction == DOWN) {
        x_next++;

    } else if (direction == LEFT) {
        y_next--;

    } else if (direction == RIGHT) {
        y_next++;
    }

    // 撞墙
    if (x_next <= 0 || row - 1 <= x_next || y_next <= 0 || column - 1 <= y_next) {
        game_data->state = END;
    }

    // 撞到自己身体
    SnakeBody *p = head;
    while (p != NULL) {
        if (x_next == p->x && y_next == p->y) {
            game_data->state = END;
            break;
        }

        p = p->next;
    }

    // 判断游戏是否结束
    if (game_data->state == END) {
        endGame(game_data);
    }

    // 吃到食物
    bool is_eat_food = false;
    if (x_next == game_data->x_food && y_next == game_data->y_food) {
        is_eat_food = true;
        createFood(game_data);
    }

    if (is_eat_food) { // 如果吃了食物
        SnakeBody *p = (SnakeBody *) malloc(sizeof(SnakeBody));
        p->x = x_next;
        p->y = y_next;
        p->next = head;

        head = p;
        game_data->head = p;

        game_data->score++;

    } else { // 如果没吃食物
        SnakeBody *p = head;
        while (p != NULL) {
            int temp_x = p->x;
            int temp_y = p->y;

            p->x = x_next;
            p->y = y_next;

            x_next = temp_x;
            y_next = temp_y;

            p = p->next;
        }
        map[x_next][y_next] = ' '; // 清除蛇尾
    }

    map[head->x][head->y] = '#';
    map[head->next->x][head->next->y] = '*';
}

void drawMap(GameData *game_data) {
    int row = game_data->row;
    int column = game_data->column;

    char (*map)[column] = (char (*)[column]) game_data->map;

    showCursor(false);
    system("clear");

    for (int i = 0; i < row; ++i) {
        for (int j = 0; j < column; ++j) {
            printf("%c", map[i][j]);
        }
        printf("\n");
    }
}

void *getDirectionAsyn(void *args) {
    GameData *game_data = (GameData *) args;

    Direction *direction;

    while (true) {
        if (game_data->state == END) {
            break;
        }

        direction = (Direction *) &game_data->direction;

        if ((GetAsyncKeyState(VK_UP) & 0x8000) && *direction != DOWN) {
            *direction = UP;

        } else if ((GetAsyncKeyState(VK_DOWN) & 0x8000) && *direction != UP) {
            *direction = DOWN;

        } else if ((GetAsyncKeyState(VK_LEFT) & 0x8000) && *direction != RIGHT) {
            *direction = LEFT;

        } else if ((GetAsyncKeyState(VK_RIGHT) & 0x8000) && *direction != LEFT) {
            *direction = RIGHT;
        }

        usleep(50 * 1000);
    }
}

void showCursor(bool visible) {
    // CONSOLE_CURSOR_INFO 结构体包含控制台光标信息, 成员分别表示光标百分比厚度和是否可见
    CONSOLE_CURSOR_INFO cursor_info = {20, visible};
    // SetConsoleCursorInfo 设定控制台窗口的光标大小和是否可见
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}